home *** CD-ROM | disk | FTP | other *** search
/ Gamers Delight 2 / Gamers Delight 2.iso / Aminet / game / role / pinfocom_3_0.lha / Source / readline.c < prev    next >
C/C++ Source or Header  |  1992-10-22  |  12KB  |  564 lines

  1. /* readline.c
  2.  *
  3.  *  ``pinfocom'' -- a portable Infocom Inc. data file interpreter.
  4.  *  Copyright (C) 1987-1992  InfoTaskForce
  5.  *
  6.  *  This program is free software; you can redistribute it and/or modify
  7.  *  it under the terms of the GNU General Public License as published by
  8.  *  the Free Software Foundation; either version 2 of the License, or
  9.  *  (at your option) any later version.
  10.  *
  11.  *  This program is distributed in the hope that it will be useful,
  12.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  *  GNU General Public License for more details.
  15.  *
  16.  *  You should have received a copy of the GNU General Public License
  17.  *  along with this program; see the file COPYING.  If not, write to the
  18.  *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  */
  20.  
  21. /*
  22.  * $Header: RCS/readline.c,v 3.0 1992/10/21 16:56:19 pds Stab $
  23.  */
  24. #include <string.h>
  25. #include <ctype.h>
  26. #include <stdio.h>
  27. #include <errno.h>
  28. #include <readline/readline.h>
  29. #include <readline/history.h>
  30.  
  31. #include "infocom.h"
  32.  
  33.  
  34. #ifndef free
  35. extern void free();
  36. #endif
  37.  
  38.  
  39. #define LIST_INC        5
  40. #define HIST_VAR        "PI_HIST"
  41. #define COMP_VAR        "PI_WORD"
  42.  
  43. #define BIND_WORD() (rl_completion_entry_function=(Function *)tcrl_generator)
  44. #define BIND_FILE() (rl_completion_entry_function=(Function *)NULL)
  45.  
  46.  
  47. extern int rl_bind_key P((int, Function *));
  48.  
  49. static char *hist_file = NULL;
  50. static char *comp_file = NULL;
  51. static char **wordlist;
  52. static int wl_len;
  53. static int wl_max;
  54.  
  55.  
  56. /*
  57.  * Function:    irl_generator()
  58.  *
  59.  * Arguments:
  60.  *      text    Token to be matched against
  61.  *      state   New word flag
  62.  *
  63.  * Returns:
  64.  *      the next string in the wordlist of which text is a
  65.  *      prefix, or NULL if the list is exhausted
  66.  *
  67.  * Description:
  68.  *      This function is called repeatedly by readline's internal
  69.  *      command completion routine to build a list of possible
  70.  *      completions for the current input
  71.  *
  72.  * Notes:
  73.  *      Readline will free the character pointer we pass back to it
  74.  *      so it is necessary to allocate space for the match.
  75.  */
  76. static char *
  77. tcrl_generator A2(char *, text, int, state)
  78. {
  79.     static int list_index, len;
  80.     char *name = NULL;
  81.     char **cpp;
  82.  
  83.     /*
  84.      * If we're starting new then reset...
  85.      */
  86.     if (!state)
  87.     {
  88.         list_index = 0;
  89.         len = strlen(text);
  90.     }
  91.  
  92.     /*
  93.      * Return the next name which partially matches from the word list.
  94.      */
  95.     for (cpp = &wordlist[list_index++]; *cpp != NULL; ++cpp, ++list_index)
  96.         if (!strncmp(*cpp, text, len))
  97.             break;
  98.  
  99.     if (*cpp != NULL)
  100.     {
  101.         name = (char *)xmalloc(strlen(*cpp) + 1);
  102.         strcpy(name, *cpp);
  103.     }
  104.  
  105.     return (name);
  106. }
  107.  
  108. /*
  109.  * Add a new word to the completion list.
  110.  */
  111. static int
  112. tcrl_add A1(const char *, wrd)
  113. {
  114.     char **cpp;
  115.     int len;
  116.     int ret = 1;
  117.  
  118.     while (isspace(*wrd))
  119.         ++wrd;
  120.  
  121.     if (*wrd == '\0')
  122.     {
  123.         scr_putmesg("You need to supply a word to @add", 1);
  124.         return (0);
  125.     }
  126.  
  127.     if (wl_len+1 == wl_max)
  128.     {
  129.         wl_max += LIST_INC;
  130.         wordlist = (char **)xrealloc(wordlist, sizeof(char *) * wl_max);
  131.     }
  132.  
  133.     for (cpp=wordlist; *cpp != NULL; ++cpp)
  134.         if (!strcmp(*cpp, wrd))
  135.             break;
  136.  
  137.     if (*cpp != NULL)
  138.     {
  139.         char buf[81];
  140.  
  141.         sprintf(buf, "`%s' already exists in the word list", wrd);
  142.         scr_putmesg(buf, 0);
  143.         ret = 0;
  144.     }
  145.     else
  146.     {
  147.         len = strlen(wrd) + 1;
  148.         wordlist[wl_len] = xmalloc(sizeof(char) * len);
  149.         strcpy(wordlist[wl_len++], wrd);
  150.         wordlist[wl_len] = NULL;
  151.     }
  152.  
  153.     return (ret);
  154. }
  155.  
  156. /*
  157.  * Delete a word to the completion list.
  158.  */
  159. static int
  160. tcrl_delete A1(const char *, wrd)
  161. {
  162.     char **cpp;
  163.     int ret = 1;
  164.  
  165.     while (isspace(*wrd))
  166.         ++wrd;
  167.  
  168.     if (*wrd == '\0')
  169.     {
  170.         scr_putmesg("You need to supply a word to @del", 1);
  171.         return (0);
  172.     }
  173.  
  174.     for (cpp=wordlist; *cpp != NULL; ++cpp)
  175.         if (!strcmp(*cpp, wrd))
  176.             break;
  177.  
  178.     if (*cpp == NULL)
  179.     {
  180.         char buf[81];
  181.  
  182.         sprintf(buf, "`%s' not found in the word list", wrd);
  183.         scr_putmesg(buf, 0);
  184.         ret = 0;
  185.     }
  186.     else
  187.     {
  188.         free(*cpp);
  189.         for (; *cpp != NULL; ++cpp)
  190.             cpp[0] = cpp[1];
  191.         --wl_len;
  192.     }
  193.  
  194.     return (ret);
  195. }
  196.  
  197. /*
  198.  * Set the history filename
  199.  */
  200. static int
  201. tcrl_hset A1(const char *, fnm)
  202. {
  203.     while (isspace(*fnm))
  204.         ++fnm;
  205.  
  206.     if (*fnm == '\0')
  207.     {
  208.         scr_putmesg("You need to supply a filename to @hset", 1);
  209.         return (0);
  210.     }
  211.  
  212.     if (hist_file)
  213.         free(hist_file);
  214.  
  215.     hist_file = xmalloc(strlen(fnm) + 1);
  216.     strcpy(hist_file, fnm);
  217.  
  218.     return (1);
  219. }
  220.  
  221. /*
  222.  * Set the completion filename
  223.  */
  224. static int
  225. tcrl_cset A1(const char *, fnm)
  226. {
  227.     while (isspace(*fnm))
  228.         ++fnm;
  229.  
  230.     if (*fnm == '\0')
  231.     {
  232.         scr_putmesg("You need to supply a filename to @cset", 1);
  233.         return (0);
  234.     }
  235.  
  236.     if (comp_file)
  237.         free(comp_file);
  238.  
  239.     comp_file = xmalloc(strlen(fnm) + 1);
  240.     strcpy(comp_file, fnm);
  241.  
  242.     return (1);
  243. }
  244.  
  245. /*
  246.  * Pre-process command line args: just load any environment variables
  247.  * so they'll be properly overridden by the command line.
  248.  */
  249. int
  250. tcrl_cmdarg A2(int, argc, char ***, argvp)
  251. {
  252.     extern char *getenv P((const char *));
  253.  
  254.     const char *fnm;
  255.  
  256.     if ((fnm = getenv(HIST_VAR)) != NULL)
  257.         tcrl_hset(fnm);
  258.  
  259.     if ((fnm = getenv(COMP_VAR)) != NULL)
  260.         tcrl_cset(fnm);
  261.  
  262.     return (argc);
  263. }
  264.  
  265. /*
  266.  * Process command args
  267.  */
  268. void
  269. tcrl_getopt A2(int, c, const char *, arg)
  270. {
  271.     switch (c)
  272.     {
  273.         case 'C':
  274.             tcrl_cset(arg);
  275.             break;
  276.  
  277.         case 'H':
  278.             tcrl_hset(arg);
  279.             break;
  280.     }
  281. }
  282.  
  283. /*
  284.  * Set up readline items (load history file, load command completion
  285.  * file, initialize history, etc.
  286.  */
  287. void
  288. tcrl_begin()
  289. {
  290.     /*
  291.      * Initialize the history and, if the user specified one, read in
  292.      * the history file.
  293.      */
  294.     using_history();
  295.     if (hist_file != NULL)
  296.     {
  297.         int err;
  298.  
  299.         if (((err = read_history(hist_file)) != 0) && (err != 2))
  300.         {
  301.             errno = err;
  302.             perror("read_history");
  303.             exit(1);
  304.         }
  305.     }
  306.  
  307.     /*
  308.      * Initialize the word completion library.
  309.      */
  310.     wordlist = (char **)xmalloc(sizeof(char *) * LIST_INC);
  311.     *wordlist = NULL;
  312.     wl_max = LIST_INC;
  313.  
  314.     /*
  315.      * If the user specified a completion file, open and read it
  316.      */
  317.     if (comp_file != NULL)
  318.     {
  319.         FILE *fp;
  320.  
  321.         if ((fp = fopen(comp_file, "r")) == NULL)
  322.         {
  323.             char buf[81];
  324.  
  325.             sprintf(buf, "Couldn't open completion file `%s'", comp_file);
  326.             scr_putmesg(buf, 1);
  327.         }
  328.         else
  329.         {
  330.             char buf[256];
  331.  
  332.             while (fgets(buf, 256, fp) != NULL)
  333.             {
  334.                 buf[strlen(buf)-1] = '\0';
  335.                 tcrl_add(buf);
  336.             }
  337.             fclose(fp);
  338.         }
  339.     }
  340.  
  341.     BIND_WORD();
  342. }
  343.  
  344. /*
  345.  * Finish up readline stuff
  346.  */
  347. void
  348. tcrl_end()
  349. {
  350.     if (hist_file != NULL)
  351.         write_history(hist_file);
  352.  
  353.     if (comp_file != NULL)
  354.     {
  355.         FILE *fp;
  356.  
  357.         if ((fp = fopen(comp_file, "w")) == NULL)
  358.         {
  359.             char buf[81];
  360.  
  361.             sprintf(buf, "Couldn't open completion file `%s'", comp_file);
  362.             scr_putmesg(buf, 1);
  363.         }
  364.         else
  365.         {
  366.             char **cpp;
  367.  
  368.             for (cpp=wordlist; *cpp != NULL; ++cpp)
  369.             {
  370.                 fputs(*cpp, fp);
  371.                 fputc('\n', fp);
  372.             }
  373.             fclose(fp);
  374.         }
  375.     }
  376. }
  377.  
  378. /*
  379.  * Print out any extra escape commands the user can use
  380.  */
  381. void
  382. tcrl_pr_escape()
  383. {
  384.     char buf[81+MAXPATHLEN];
  385.  
  386.     scr_putline("  add <word>:    Add <word> to completion list");
  387.     scr_putline("  del <word>:    Delete <word> from completion list");
  388.  
  389.     sprintf(buf, "  hset <file>:   Set history save file name [%s]",
  390.             hist_file == NULL ? "" : hist_file);
  391.     scr_putline(buf);
  392.  
  393.     sprintf(buf, "  cset <file>:   Set completion save file name [%s]",
  394.             comp_file == NULL ? "" : comp_file);
  395.     scr_putline(buf);
  396.  
  397.     scr_putline("");
  398. }
  399.  
  400. /*
  401.  * Read a line from readline.  Maybe add to history; maybe allow
  402.  * history expansion, etc.
  403.  *
  404.  * Returns -1 if we need to re-print the prompt (history listing), or
  405.  * # of chars read into buffer.
  406.  */
  407. int
  408. scr_getstr A4( const char *, prompt,
  409.                int, length,
  410.                char *, buffer,
  411.                Bool, is_filenm )
  412. {
  413.     char *bp = buffer;
  414.     char *command;
  415.     char *cp;
  416.     int len = 0;
  417.  
  418.     if (is_filenm)
  419.         BIND_FILE();
  420.  
  421.     /*
  422.      * Get a line of text; if nothing is returned then return an empty
  423.      * buffer.
  424.      */
  425.     if ((cp = readline(prompt)) == NULL)
  426.     {
  427.         *buffer = '\0';
  428.         return (0);
  429.     }
  430.  
  431.     /*
  432.      * If we're getting a filename history is not allowed so simply
  433.      * copy over the buffer.  Otherwise, perform history processing.
  434.      */
  435.     if (is_filenm)
  436.     {
  437.         BIND_WORD();
  438.         command = cp;
  439.         for (; *cp && (len < length-1); ++len, ++bp, ++cp)
  440.             *bp = *cp;
  441.         *bp = '\0';
  442.     }
  443.     else
  444.     {
  445.         /*
  446.          * I add the command to the history even if it's an invalid
  447.          * history expansion, because I hate the fact that bash, etc.
  448.          * don't: if you screw up you have to retype the thing from
  449.          * scratch!  (isn't the the whole point of history, so you
  450.          * *don't* have to do that?)
  451.          */
  452.         if ((len = history_expand(cp, &command)) == -1)
  453.         {
  454.             printf("history: %s\n\n", command);
  455.             add_history(cp);
  456.             free(command);
  457.             free(cp);
  458.             return (-1);
  459.         }
  460.         free(cp);
  461.  
  462.         /*
  463.          * If some kind of expansion was done, then print out the
  464.          * resulting command FYI the user...
  465.          */
  466.         if (len)
  467.         {
  468.             printf("(%s)\n", command);
  469.         }
  470.  
  471.         /*
  472.          * Unfortunately, contrary to the documentation for history,
  473.          * the history_expand() function leaves all backslashes which
  474.          * escape !'s *in* the string; so if we find one we have to
  475.          * skip it.
  476.          */
  477.         for (cp=command, len=0; *cp && (len < length-1); ++len, ++bp, ++cp)
  478.         {
  479.             *bp = *cp;
  480.             if ((*cp == '\\') && (cp[1] == '!'))
  481.             {
  482.                 --bp;
  483.                 --len;
  484.             }
  485.         }
  486.         *bp = '\0';
  487.     }
  488.  
  489.     /*
  490.      * If there's more beyond the max length of our buffer,
  491.      * throw it away...
  492.      */
  493.     if (*cp != '\0')
  494.     {
  495.         printf(" [ Input line too long.  Flushing: `%s", cp);
  496.         scr_putline("' ]");
  497.     }
  498.  
  499.     free(command);
  500.  
  501.     /*
  502.      * If we got some text and we're not doing filename processing,
  503.      * then check for a history list request and add the command into
  504.      * history.  Also check for an escape line; if we find one process
  505.      * it.
  506.      */
  507.     if (!is_filenm && len)
  508.     {
  509.         if ((len == 1) && (buffer[0] == '?'))
  510.         {
  511.             HIST_ENTRY **hist;
  512.  
  513.             if ((hist = history_list()) == NULL)
  514.             {
  515.                 scr_putline("You have no history.");
  516.             }
  517.             else
  518.             {
  519.                 int i;
  520.  
  521.                 for (i=history_base; *hist != NULL; ++i, ++hist)
  522.                 {
  523.                     printf("%5d  ", i);
  524.                     scr_putline((*hist)->line);
  525.                 }
  526.             }
  527.             return (-1);
  528.         }
  529.  
  530.         add_history(buffer);
  531.  
  532.         if ((len > 3) && (buffer[0] == ESC_CHAR[0]))
  533.         {
  534.             int (*fnp) P((const char *)) = NULL;
  535.             char c;
  536.  
  537.             for (cp = &buffer[1]; (*cp != '\0') && !isspace(*cp); ++cp)
  538.             {}
  539.  
  540.             c = *cp;
  541.             *cp = '\0';
  542.             if (!strcmp(&buffer[1], "add"))
  543.                 fnp = tcrl_add;
  544.             else if (!strcmp(&buffer[1], "del"))
  545.                 fnp = tcrl_delete;
  546.             if (!strcmp(&buffer[1], "hset"))
  547.                 fnp = tcrl_hset;
  548.             else if (!strcmp(&buffer[1], "cset"))
  549.                 fnp = tcrl_cset;
  550.             *cp = c;
  551.  
  552.             if (fnp != NULL)
  553.             {
  554.                 if ((*fnp)(cp))
  555.                     scr_putline("");
  556.  
  557.                 len = -1;
  558.             }
  559.         }
  560.     }
  561.  
  562.     return (len);
  563. }
  564.